package ch.sv7.tool.dbmanager.db;

import java.sql.DatabaseMetaData;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ch.sv7.tool.dbmanager.StringUtil;
import ch.sv7.tool.dbmanager.db.difference.ForeignKeyDeferrabilityDifference;
import ch.sv7.tool.dbmanager.db.difference.ForeignKeyDeleteRuleDifference;
import ch.sv7.tool.dbmanager.db.difference.ForeignKeyNameDifference;
import ch.sv7.tool.dbmanager.db.difference.ForeignKeyUpdateRuleDifference;
import ch.sv7.tool.dbmanager.db.difference.SchemaDifferences;

/**
 * This class represent a foreign key on a database table
 *
 * @author svt
 *
 */
public class ForeignKeySchema implements SchemaElement, Comparable, Identifiable {

	private TableSchema tableSchema;
	private String name;
	
	private String columns;
	private String foreignTable;
	private String foreignColumns;
	
	private String updateRule;
	private String deleteRule;
	private String deferrability;


	
	/**
	 * @param tableSchema
	 * @param name
	 * @param column
	 * @param foreignTable
	 * @param foreignColumn
	 * @param updateRule
	 * @param deleteRule
	 * @param deferrability
	 */
	public ForeignKeySchema(TableSchema tableSchema, String name, String columns, String foreignTable, String foreignColumns,
			String updateRule, String deleteRule, String deferrability) {
		super();
		this.tableSchema = tableSchema;
		this.name = name;
		this.columns = columns;
		this.foreignTable = foreignTable;
		this.foreignColumns = foreignColumns;
		this.updateRule = updateRule;
		this.deleteRule = deleteRule;
		this.deferrability = deferrability;
	}
	public ForeignKeySchema(TableSchema tableSchema, String name, String columns, String foreignTable, String foreignColumns,
			int updateRule, int deleteRule, int deferrability) {
		super();
		this.tableSchema = tableSchema;
		this.name = name;
		this.columns = columns;
		this.foreignTable = foreignTable;
		this.foreignColumns = foreignColumns;
		switch (updateRule){
		case DatabaseMetaData.importedKeyNoAction:this.updateRule = "NoAction";break;
		case DatabaseMetaData.importedKeyCascade:this.updateRule = "Cascade";break;
		case DatabaseMetaData.importedKeySetNull:this.updateRule = "SetNull";break;
		case DatabaseMetaData.importedKeySetDefault:this.updateRule = "SetDefault";break;
		case DatabaseMetaData.importedKeyRestrict:this.updateRule = "Restrict";break;
		}
		switch (deleteRule){
		case DatabaseMetaData.importedKeyNoAction:this.deleteRule = "NoAction";break;
		case DatabaseMetaData.importedKeyCascade:this.deleteRule = "Cascade";break;
		case DatabaseMetaData.importedKeySetNull:this.deleteRule = "SetNull";break;
		case DatabaseMetaData.importedKeySetDefault:this.deleteRule = "SetDefault";break;
		case DatabaseMetaData.importedKeyRestrict:this.deleteRule = "Restrict";break;
		}
		switch (deferrability){
		case DatabaseMetaData.importedKeyInitiallyDeferred:this.deferrability = "InitiallyDeferred";break;
		case DatabaseMetaData.importedKeyInitiallyImmediate:this.deferrability = "InitiallyImmediate";break;
		case DatabaseMetaData.importedKeyNotDeferrable:this.deferrability = "NotDeferrable";break;
		}
	}

	
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}
	/**
	 * @return the column
	 */
	public String getColumns() {
		return columns;
	}
	public Set getColumnsSet() {
		return StringUtil.commaSeparatedToSet(columns);
	}

	/**
	 * @return the foreignTable
	 */
	public String getForeignTable() {
		return foreignTable;
	}

	/**
	 * @return the foreignColumn
	 */
	public String getForeignColumns() {
		return foreignColumns;
	}
	public Set getForeignColumnsSet() {
		return StringUtil.commaSeparatedToSet(foreignColumns);
	}

	/**
	 * @return the updateRule
	 */
	public String getUpdateRule() {
		return updateRule;
	}

	/**
	 * @return the deleteRule
	 */
	public String getDeleteRule() {
		return deleteRule;
	}

	/**
	 * @return the deferrability
	 */
	public String getDeferrability() {
		return deferrability;
	}

	/**
	 * @return the tableSchema
	 */
	public TableSchema getTableSchema() {
		return tableSchema;
	}
	/**
	 * @see ch.sv7.tool.dbmanager.db.Identifiable#getIdentity()
	 */
	public String getIdentity() {
		return getColumns() + "->" + getForeignTable() + ":" + getForeignColumns();
	}

	/**
	 * @see ch.sv7.tool.dbmanager.db.Identifiable#getEntityType()
	 */
	public String getEntityType() {
		return "foreign key";
	}

	public String getSchemaIdentifier() {
		return "ForeignKey: " + getTableSchema().getName() + "." + getName();
	}
	
	public static void mergeForeignKeys(List foreignKeys) {
		/*
		Collections.sort(foreignKeys, new Comparator() {

			public int compare(Object o1, Object o2) {
				ForeignKeySchema i1 = (ForeignKeySchema)o1;
				ForeignKeySchema i2 = (ForeignKeySchema)o2;
				return i1.getColumns().compareTo(i2.getColumns());
			}
			
		});
		*/
		Map fkByName = new HashMap();
		Iterator iter = foreignKeys.iterator();
		while (iter.hasNext()){
			ForeignKeySchema fk = (ForeignKeySchema)iter.next();
			if (fkByName.containsKey(fk.getName())){
				ForeignKeySchema originalFK = (ForeignKeySchema)fkByName.get(fk.getName());
				originalFK.columns = originalFK.getColumns() + ", " + fk.getColumns();
				originalFK.foreignColumns = originalFK.getForeignColumns() + ", " + fk.getForeignColumns();
				iter.remove();
			} else {
				fkByName.put(fk.getName(), fk);
			}
		}
	}
	
	/**
	 * @see ch.sv7.tool.dbmanager.db.Comparable#computeDifferencesFromActual(java.lang.Object)
	 */
	public void computeDifferencesFromActual(SchemaDifferences diffs, Object object) {
		if (!(object instanceof ForeignKeySchema)){
			return;
		}
		ForeignKeySchema actual = (ForeignKeySchema)object;
		
		/*
		 * No need to process these as they are part of identity
		if (!columns.equals(actual.columns)) {
			diffs.addDifference(new ForeignKeyDifference(tableSchema,name, " column must be " + columns + " but is " + actual.columns));
		}
		if (!foreignTable.equals(actual.foreignTable)) {
			diffs.addDifference(new ForeignKeyDifference(tableSchema,name, " foreignTable must be " + foreignTable + " but is " + actual.foreignTable));
		}
		if (!foreignColumns.equals(actual.foreignColumns)) {
			diffs.addDifference(new ForeignKeyDifference(tableSchema,name, " foreignColumn must be " + foreignColumns + " but is " + actual.foreignColumns));
		}
		*/
		
		if (!name.equals(actual.name)) {
			diffs.addDifference(this, new ForeignKeyNameDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
		if (!updateRule.equals(actual.updateRule)) {
			diffs.addDifference(this, new ForeignKeyUpdateRuleDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
		if (!deleteRule.equals(actual.deleteRule)) {
			diffs.addDifference(this, new ForeignKeyDeleteRuleDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
		if (!deferrability.equals(actual.deferrability)) {
			diffs.addDifference(this, new ForeignKeyDeferrabilityDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((columns == null) ? 0 : columns.hashCode());
		result = prime * result + ((deferrability == null) ? 0 : deferrability.hashCode());
		result = prime * result + ((deleteRule == null) ? 0 : deleteRule.hashCode());
		result = prime * result + ((foreignColumns == null) ? 0 : foreignColumns.hashCode());
		result = prime * result + ((foreignTable == null) ? 0 : foreignTable.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((updateRule == null) ? 0 : updateRule.hashCode());
		return result;
	}
	
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ForeignKeySchema other = (ForeignKeySchema) obj;
		if (columns == null) {
			if (other.columns != null)
				return false;
		} else if (!columns.equals(other.columns))
			return false;
		if (deferrability == null) {
			if (other.deferrability != null)
				return false;
		} else if (!deferrability.equals(other.deferrability))
			return false;
		if (deleteRule == null) {
			if (other.deleteRule != null)
				return false;
		} else if (!deleteRule.equals(other.deleteRule))
			return false;
		if (foreignColumns == null) {
			if (other.foreignColumns != null)
				return false;
		} else if (!foreignColumns.equals(other.foreignColumns))
			return false;
		if (foreignTable == null) {
			if (other.foreignTable != null)
				return false;
		} else if (!foreignTable.equals(other.foreignTable))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (updateRule == null) {
			if (other.updateRule != null)
				return false;
		} else if (!updateRule.equals(other.updateRule))
			return false;
		return true;
	}


	
	
}
